package br.ufpe.cin.mac.util;

import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.CharacterCodingException;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * Class Message
 * 
 * @author Osmany
 * @author Rafael Roque
 * 
 */
public class Message implements Serializable, Comparable<Message> {

	private static final long serialVersionUID = 1L;

	private int msgId;

	private Integer index;

	private String type;

	private List<String> interceptors;

	private List<List<String>> operators;

	private String receiver, sender, msg, connector;

	private byte[][] bParam;

	private byte[][] bSig;

	private byte[] bMethod;

	private byte key[];

	public Message() {
		this("", "", "");
	}

	public Message(String sender, String receiver, String msg) {
		this.receiver = receiver;
		this.sender = sender;
		this.msg = msg;
		this.interceptors = Collections
				.synchronizedList(new LinkedList<String>());
		this.operators = new LinkedList<List<String>>();
		this.index = new Integer(0);
		this.msgId = (int) System.currentTimeMillis() + this.hashCode();
		this.setType(Constants.NEW_MESSAGE);
	}
	
	public void removeOperators(){
		this.operators = new LinkedList<List<String>>();
	}
	public void addOperation(List<String> ops) {
		this.operators.add(ops);
	}

	public byte[] getKey() {
		return key;
	}

	public void setKey(byte[] key) {
		this.key = key;
	}

	public void setConnector(String ctr) {
		this.connector = ctr;
	}

	public String getConnector() {
		return this.connector;
	}

	public int getMsgId() {
		return this.msgId;
	}

	public void setMsgId(int msgId) {
		this.msgId = msgId;
	}

	public List<String> getInterceptors() {
		return interceptors;
	}

	public String getMsg() {
		return msg;
	}

	public List<List<String>> getOperators() {
		return operators;
	}

	public String readInterceptor() {
		try {
			String inter = interceptors.get(index);
			//System.out.println(inter + " " + index);
			return inter;
		} catch (Exception e) {
			return "Empty";
		}
	}

	public void addInterceptor(String inter, List<String> op) {
		interceptors.add(inter);
		operators.add(interceptors.indexOf(inter), op);
	}

	public List<String> readOperations() {
		if (operators.size() > 0) {
			List<String> ops = operators.get(index);
			//System.out.println(ops + " " + index);
			index = index + 1;
			return ops;
		}
		return new LinkedList<String>();
	}

	@Override
	public String toString() {

		StringBuffer buf = new StringBuffer();
		buf.append("------\nMSG\n\n");
		buf.append("ID: " + getMsgId());
		buf.append("\n");

		buf.append("Index:  " + getIndex());
		buf.append("\n");

		buf.append("Type: " + getType());
		buf.append("\n");

		buf.append("Interceptors\n");
		Iterator<String> ite1 = interceptors.iterator();
		int i = 0;
		while (ite1.hasNext()) {
			buf.append("Inter[" + (i++) + "]: " + ite1.next());
		}
		buf.append("\n");

		buf.append("Operators\n");
		Iterator<List<String>> ite2 = operators.iterator();
		i = 0;
		int j = 0;
		while (ite2.hasNext()) {
			buf.append("Operator[" + i + "]: " + ite2.next() + "\n");
			Iterator ite3 = ite2.next().iterator();
			while (ite3.hasNext()) {
				buf.append("Operator[" + i + "][" + j + "]:  " + ite3.next()
						+ "\n");
				j++;
			}
			i++;
		}
		buf.append("\n\n");

		buf.append("Receiver: " + receiver);
		buf.append("\n");

		buf.append("Sender: " + sender);
		buf.append("\n");

		buf.append("MSG: " + msg);
		buf.append("\n");

		buf.append("CONNECTOR: " + connector);
		buf.append("\n");

		Object[] objs = getParameter();
		buf.append("\nParameters\n");
		if (objs != null) {
			for (i = 0; i < objs.length; i++) {
				buf.append("Obj[" + i + "]: " + objs[i].toString() + "\n");
			}
		}
		buf.append("\n\n");

		buf.append("Signatures\n");
		String str[] = getSignature();
		if (str != null) {
			for (i = 0; i < str.length; i++) {
				buf.append("Sig[" + i + "]: " + str[i] + "\n");
			}
		}
		buf.append("\n\n");

		buf.append("Method: " + getMethod());
		buf.append("\n");

		buf.append("Key: " + getKey());
		buf.append("\n------\n\n");

		return buf.toString();
	}

	/*
	 * Observar
	 */
	public void removeAllInterceptors() {
		interceptors.clear();
		operators.clear();
		index = 0;
	}

	public String getSender() {
		return sender;
	}

	public void setSender(String Sender) {
		this.sender = Sender;
	}

	public String getReceiver() {
		return receiver;
	}

	public void setReceiver(String Receiver) {
		this.receiver = Receiver;
	}

	public String getMessage() {
		return msg;
	}

	public Object[] getParameter() {
		Object[] ret = null;
		try {
			ret = Marshal.recoverObjectArray(bParam);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return ret;
	}

	public void setParameter(Object[] param) {
		byte[][] bParam = null;
		try {
			bParam = Marshal.serializeObjectArray(param);
		} catch (IOException e) {
			e.printStackTrace();
		}
		this.bParam = bParam;
	}

	public String[] getSignature() {
		String[] sig = null;
		try {
			sig = Marshal.recoverStringArray(bSig);
		} catch (CharacterCodingException e) {
			e.printStackTrace();
		}
		return sig;
	}

	public Class[] getSignatureClasses() {
		Class<?>[] ret;
		String[] sig = getSignature();

		ret = new Class<?>[sig.length];
		for (int i = 0; i < sig.length; i++) {
			try {
				ret[i] = Class.forName(sig[i]);
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return ret;
	}

	public void setSignature(String[] sig) {
		byte[][] bSig = null;
		try {
			bSig = Marshal.serializeStringArray(sig);
		} catch (CharacterCodingException e) {
			e.printStackTrace();
		}
		this.bSig = bSig;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getMethod() {
		String ret = null;
		try {
			ret = Marshal.recoverString(bMethod);
		} catch (CharacterCodingException e) {
			e.printStackTrace();
		}
		return ret;
	}

	public void setMethod(String method) {
		byte[] bMethod = null;
		try {
			bMethod = Marshal.serializeString(method);
		} catch (CharacterCodingException e) {
			e.printStackTrace();
		}
		this.bMethod = bMethod;
	}

	public Integer getIndex() {
		return index;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	public byte[][] getBParam() {
		return bParam;
	}

	public void setBParam(byte[][] param) {
		bParam = param;
	}

	public byte[][] getBSig() {
		return bSig;
	}

	public void setBSig(byte[][] sig) {
		bSig = sig;
	}

	public byte[] getBMethod() {
		return bMethod;
	}

	public void setBMethod(byte[] method) {
		bMethod = method;
	}

	public Class[] getClassesParam() {
		Object[] param = getParameter();
		Class[] cls = new Class[param.length];
		for (int i = 0; i < cls.length; i++) {
			if (param[i] != null) {
				cls[i] = param[i].getClass();
			} else {
				cls[i] = null;
			}
		}

		return cls;

	}

	public int compareTo(Message msg) {
		return this.index.compareTo(msg.index);
	}
}
